﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using UnityEngine.Networking;

public static class TimeUtils
{
    private static readonly DateTime EPOCH_DATE_TIME = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime EPOCH_DATE_TIME2 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static long epoch => EPOCH_DATE_TIME.Ticks;
    private static long epochZero => EPOCH_DATE_TIME2.Ticks;

    #region Server Time

    private static List<string> _serverUrlList = new List<string> // 网络服务器URL列表
        {
            "https://www.baidu.com", "https://github.com",
            "https://www.microsoft.com", "https://www.google.com"
        };

    private static long _serverTimeAtStartup = 0L; // 启动时的服务器时间
    private static long _clientTimeAtStartup = 0L; // 启动时的客户端时间 

    public static long ServerNowInSecond()
    {
        return (_serverTimeAtStartup + (NowInSecondsLocal() - _clientTimeAtStartup) * 1000) / 1000;
    }

    public static long ServerNow()
    {
        return _serverTimeAtStartup + (NowInSecondsLocal() - _clientTimeAtStartup) * 1000;
    }

    /// <summary>
    /// 初始化启动时的服务器时间
    /// </summary>
    public static void InitServerTime()
    {
        if (_serverTimeAtStartup != 0) return;
        // foreach (var url in _serverUrlList)
        // {
        //     HttpRequestManager.Instance.Get(url, new HttpRequestCallImpl((UnityWebRequest resp) =>
        //     {
        //         if (_serverTimeAtStartup != 0) return;
        //         var headers = resp.GetResponseHeaders();
        //         var key = "DATE";
        //         string value = null;
        //         if (headers != null && headers.ContainsKey(key))
        //         {
        //             headers.TryGetValue(key, out value);
        //         }
        //
        //         if (value != null)
        //         {
        //             var split = value.Split(',');
        //             var sp2 = split[1];
        //             sp2 = sp2.Replace("GMT", "");
        //             DateTime.TryParse(sp2, out DateTime date);
        //             var totalSec = (date.Ticks - epoch) / 10000;
        //             _serverTimeAtStartup = totalSec;
        //             _clientTimeAtStartup = NowInSecondsLocal();
        //         }
        //     }, (code, msg) => { }));   
        // }
    }

    #endregion

    /// <summary>
    /// 客户端时间(Utc0)
    /// </summary>
    /// <returns></returns>
    public static long NowInSeconds()
    {
        return _serverTimeAtStartup == 0 ? (DateTime.UtcNow.Ticks - epoch) / 10000000 : ServerNowInSecond();
    }

    public static long Now()
    {
        return _serverTimeAtStartup == 0 ? (DateTime.UtcNow.Ticks - epoch) / 10000 : ServerNow();
    }

    public static long NowInSecondsLocal()
    {
        return (DateTime.Now.Ticks - epochZero) / 10000000;
    }

    public static long NowLocal()
    {
        return (DateTime.Now.Ticks - epochZero) / 10000;
    }

    public static long TimeStamp => (DateTime.UtcNow.Ticks - epoch) / 10000;

    /// <summary>
    /// 时间戳转换为本地时间对象
    /// </summary>
    /// <returns></returns>      
    [Obsolete]
    public static DateTime GetUnixDateTime(long unix)
    {
        //long unix = 1500863191;
        DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
        DateTime newTime = dtStart.AddMilliseconds(unix);
        return newTime;
    }

    /// <summary>
    /// 时间戳转换为Utc时间对象
    /// </summary>
    /// <returns></returns>    
    public static DateTime GetUtcDateTime(long unix)
    {
        var startDt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        return startDt.AddMilliseconds(unix);
    }

    /// <summary>
    /// 今天距下一个周 dayOfWeek 还有几天
    /// </summary>
    /// <param name="dayOfWeek">1-7</param>
    /// <returns></returns>
    public static int TodayToAnyWeekDay(int dayOfWeek)
    {
        dayOfWeek = dayOfWeek < 1 ? 1 : dayOfWeek;
        dayOfWeek = dayOfWeek > 7 ? 7 : dayOfWeek;
        int days = dayOfWeek - TodayOfWeek();
        days = days <= 0 ? days + 7 : days;
        return days;
    }
    
    /// <summary>
    /// 今天距下一个周 dayOfWeek 的s数
    /// </summary>
    /// <param name="dayOfWeek"></param>
    /// <returns></returns>
    public static long TodayToAnyWeekDaySeconds(int dayOfWeek)
    {
        dayOfWeek = dayOfWeek < 1 ? 1 : dayOfWeek;
        dayOfWeek = dayOfWeek > 7 ? 7 : dayOfWeek;
        int days = dayOfWeek - TodayOfWeek();
        days = days < 0 ? days + 7 : days;
        return days * 24 * 60 * 60 + EndOfToday();
    }
    
    public static string TodayToAnyWeekDaySmartFormat(int dayOfWeek)
    {
        return SmartTimeFormat(TodayToAnyWeekDaySeconds(dayOfWeek));
    }

    public static int TodayOfWeek()
    {
        return GetDayOfWeek(NowInSeconds());
    }

    /// <summary>
    /// 获取当前时间戳是一周中的哪一天
    /// </summary>
    /// <param name="unix">单位s</param>
    /// <returns></returns>
    public static int GetDayOfWeek(long unix)
    {
        var week = (int)GetUtcDateTime(unix * 1000).DayOfWeek;
        week = week == 0 ? 7 : week; // 星期天特殊处理
        return week;
    }

    /// <summary>
    /// 获取当前时间戳是一个月中的哪一天
    /// </summary>
    /// <param name="unix"></param>
    /// <returns></returns>
    public static int GetDayOfMonth(long unix)
    {
        var day = GetUtcDateTime(unix * 1000).Day;
        return day;
    }

    /// <summary>
    /// 获取当前月份有多少天
    /// </summary>
    /// <param name="unix"></param>
    /// <returns></returns>
    public static int GetDaysOfMonth(long unix)
    {
        var dt = GetUtcDateTime(unix * 1000);
        return DateTime.DaysInMonth(dt.Year, dt.Month);
    }

    /// <summary>
    /// 当前时间所在的周数
    /// </summary>
    /// <param name="unix">s</param>
    /// <returns></returns>
    public static int WeekOfYear(long unix)
    {
        var dateTime = GetUtcDateTime(unix*1000);
        // 将给定日期转换为该年的第一天
        DateTime firstDay = new DateTime(dateTime.Year, 1, 1);
        // 如果1月1日是周三或更晚，则第一周应该包含1月4日
        firstDay = firstDay.DayOfWeek >= DayOfWeek.Wednesday ? firstDay.AddDays(4 - (int)firstDay.DayOfWeek) : firstDay.AddDays(1 - (int)firstDay.DayOfWeek);
        // 计算给定日期与该年的第一个周一之间的天数差
        int diff = (dateTime - firstDay).Days + 1;
        // 返回周数
        return (diff / 7) + ((diff % 7 > 0) ? 1 : 0);
    } 

    /// <summary>
    /// 天数转为秒
    /// </summary>
    /// <param name="day"></param>
    /// <returns></returns>
    public static long GetSecondsByDay(int day)
    {
        if (day <= 0) return 0;

        return day * 24 * 3600;
    }
    
    /// <summary>
    /// 获取一天的结束时间
    /// </summary>
    /// <param name="unix">s</param>
    /// <returns></returns>
    public static long EndOfDay(long unix)
    {
        var dateTime = GetUtcDateTime(unix*1000);
        return (new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59, dateTime.Kind).Ticks - epoch) / 10000000;
    }
    
    /// <summary>
    /// 距离今天结束还有多长时间
    /// </summary>
    /// <returns></returns>
    public static long EndOfToday()
    {
        // 24 - 16'26''48 = 7'33'12
        var dateTime = DateTime.UtcNow;
        return (new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59, dateTime.Kind).Ticks - epoch) / 10000000 - NowInSeconds();
    }

    #region SmartTimeFormat

    /// <summary>
    /// 智能时间格式化
    /// </summary>
    /// <param name="second"></param>
    /// <returns></returns>
    public static string SmartTimeFormat(long second, string def = "")
    {
        if (second < 0) return def;
        long day = second / 86400;
        long hour = second % 86400 / 3600;
        long min = second % 86400 % 3600 / 60;
        long sec = second % 60;
        var result = "Full";
        if (day == 0 && hour == 0) // 小于1小时 xx:xx
        {
            result = SecondFormatMmSs(second);
        }

        if (day == 0 && hour > 0) // 大于1小时,小于1天 xxhxxm
        {
            result = SecondFormatHhMm(second);
        }

        if (day > 0) // 大于24小时 xxdxxh
        {
            result = SecondFormatDdHh(second);
        }

        return result;
    }

    public static string SecondFormatHhMmSs2(long second)
    {
        string str = "";
        long hour = second / 3600;
        long min = second % 3600 / 60;
        long sec = second % 60;
        if (hour < 10)
        {
            str += "0" + hour.ToString();
        }
        else
        {
            str += hour.ToString();
        }

        str += "h ";
        if (min < 10)
        {
            str += "0" + min.ToString();
        }
        else
        {
            str += min.ToString();
        }

        str += "m ";
        if (sec < 10)
        {
            str += "0" + sec.ToString();
        }
        else
        {
            str += sec.ToString();
        }

        str += "s";
        return str;
    }

    public static string SecondFormatDdHhMm(long second)
    {
        string str = "";
        long day = second / 86400;
        long hour = second % 86400 / 3600;
        long min = second % 86400 % 3600 / 60;
        if (day < 10)
        {
            str += "0" + day.ToString();
        }
        else
        {
            str += day.ToString();
        }

        str += ":";
        if (hour < 10)
        {
            str += "0" + hour.ToString();
        }
        else
        {
            str += hour.ToString();
        }

        str += ":";
        if (min < 10)
        {
            str += "0" + min.ToString();
        }
        else
        {
            str += min.ToString();
        }

        return str;
    }

    public static string SecondFormatHhMmSs(long second)
    {
        StringBuilder sb = new StringBuilder();
        long hour = second / 3600;
        long min = second % 3600 / 60;
        long sec = second % 60;
        if (hour < 10)
        {
            sb.Append("0").Append(hour);
        }
        else
        {
            sb.Append(hour);
        }

        sb.Append(":");
        if (min < 10)
        {
            sb.Append("0").Append(min);
        }
        else
        {
            sb.Append(min);
        }

        sb.Append(":");
        if (sec < 10)
        {
            sb.Append("0").Append(sec);
        }
        else
        {
            sb.Append(sec);
        }

        return sb.ToString();
    }

    public static string SecondFormatMmSs(long second)
    {
        string str = "";
        long min = second / 60;
        long sec = second % 60;
        if (min < 10)
        {
            str += "0" + min.ToString();
        }
        else
        {
            str += min.ToString();
        }

        str += ":";
        if (sec < 10)
        {
            str += "0" + sec.ToString();
        }
        else
        {
            str += sec.ToString();
        }

        return str;
    }

    public static string SecondFormatDdHhMmSs(long second)
    {
        string str = "";
        long day = second / 86400;
        long hour = second % 86400 / 3600;
        long min = second % 86400 % 3600 / 60;
        long sec = second % 60;
        if (day < 10)
        {
            str += "0" + day.ToString();
        }
        else
        {
            str += day.ToString();
        }

        str += "d ";
        if (hour < 10)
        {
            str += "0" + hour.ToString();
        }
        else
        {
            str += hour.ToString();
        }

        str += "h ";
        if (min < 10)
        {
            str += "0" + min.ToString();
        }
        else
        {
            str += min.ToString();
        }

        str += "m ";
        if (sec < 10)
        {
            str += "0" + sec.ToString();
        }
        else
        {
            str += sec.ToString();
        }

        str += "s";
        return str;
    }

    public static string SecondFormatDdHhMm2(long second)
    {
        string str = "";
        long day = second / 86400;
        long hour = second % 86400 / 3600;
        long min = second % 86400 % 3600 / 60;
        long sec = second % 60;
        if (day < 10)
        {
            str += "0" + day.ToString();
        }
        else
        {
            str += day.ToString();
        }

        str += "d ";
        if (hour < 10)
        {
            str += "0" + hour.ToString();
        }
        else
        {
            str += hour.ToString();
        }

        str += "h ";
        if (min < 10)
        {
            str += "0" + min.ToString();
        }
        else
        {
            str += min.ToString();
        }

        return str;
    }

    public static string SecondFormatDdHh(long second)
    {
        string str = "";
        long day = second / 86400;
        long hour = second % 86400 / 3600;
        long min = second % 86400 % 3600 / 60;
        long sec = second % 60;
        // if (day < 10)
        // {
        //     str += "0" + day.ToString();
        // }
        // else
        // {
        //     str += day.ToString();
        // }

        str += day.ToString();

        str += "d ";
        // if (hour < 10)
        // {
        //     str += "0" + hour.ToString();
        // }
        // else
        // {
        //     str += hour.ToString();
        // }

        if (hour > 0) str += $"{hour.ToString()}h";
        return str;
    }

    public static string SecondFormatHhMm(long second)
    {
        string str = "";
        long day = second / 86400;
        long hour = second % 86400 / 3600;
        long min = second % 86400 % 3600 / 60;
        long sec = second % 60;

        // if (hour < 10)
        // {
        //     str += "0" + hour.ToString();
        // }
        // else
        // {
        //     str += hour.ToString();
        // }
        str += hour.ToString();

        str += "h ";
        // if (min < 10)
        // {
        //     str += "0" + min.ToString();
        // }
        // else
        // {
        //     str += min.ToString();
        // }
        if (min > 0) str += $"{min.ToString()}m";
        return str;
    }
    
    #endregion

    /// <summary>
    /// 获取当前日期(年月日)
    /// </summary>
    /// <param name="format">格式(年月日,年/月/日,年-月-日 等等)</param>
    /// <returns></returns>
    public static string GetCurrentDate(string format = "yyyyMMdd")
    {
        return DateTime.Now.ToString(format);
    }

    public static long GetDayRemainingTime()
    {
        //获取当前时间
        DateTime DateTime1 = DateTime.Now;
        //第二天的0点00分00秒
        DateTime DateTime2 = DateTime.Now.AddDays(1).Date;
        //把2个时间转成TimeSpan,方便计算
        TimeSpan ts1 = new TimeSpan(DateTime1.Ticks);
        TimeSpan ts2 = new TimeSpan(DateTime2.Ticks);
        //时间比较，得出差值
        TimeSpan ts = ts1.Subtract(ts2).Duration();
        //结果
        return (long)ts.TotalSeconds;
    }

    // 和lastTime比较,是否是新的一天
    public static bool IsNewDay(long lastTime)
    {
        if (lastTime == 0)
        {
            return true;
        }

        string lastDay = GetUtcDateTime(lastTime * 1000).ToShortDateString();
        DateTimeFormatInfo dtFormat = new DateTimeFormatInfo();
        dtFormat.ShortDatePattern = "yyyy/MM/dd";
        DateTime lastDt = Convert.ToDateTime(lastDay, dtFormat).ToUniversalTime();
        TimeSpan ts = lastDt - new DateTime(1970, 1, 1);
        long lastMs = (long)ts.TotalMilliseconds;

        // string stringNow = DateTime.UtcNow.ToShortDateString();
        string stringNow = GetUtcDateTime(Now()).ToShortDateString();
        DateTime nowDt = Convert.ToDateTime(stringNow, dtFormat).ToUniversalTime();
        TimeSpan nowTs = nowDt - new DateTime(1970, 1, 1);
        long nowMs = (long)nowTs.TotalMilliseconds;
        return nowMs > lastMs;
    }

    public static string ConvertMs(long totalMs, bool withHour = true)
    {
        totalMs /= 1000;
        long minute = totalMs / 60;
        long hour = minute / 60;
        minute %= 60;
        long second = totalMs % 60;
        if (withHour) return $"{hour:D2}:{minute:D2}:{second:D2}";
        return minute < 100 ? $"{minute:D2}:{second:D2}" : $"{minute}:{second:D2}";
    }

    public static long ConvertMs(DateTime dateTime)
    {
        return (long)(dateTime - EPOCH_DATE_TIME2).TotalMilliseconds;
    }
}